package com.ak.es.repository;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.filter.Filters;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.cardinality.Cardinality;
import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.max.ParsedMax;
import org.elasticsearch.search.aggregations.metrics.min.ParsedMin;
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile;
import org.elasticsearch.search.aggregations.metrics.percentiles.PercentileRanks;
import org.elasticsearch.search.aggregations.metrics.percentiles.PercentileRanksAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles;
import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.stats.Stats;
import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.ak.es.annotation.ESMapping;
import com.ak.es.entity.Down;
import com.ak.es.entity.HighLight;
import com.ak.es.entity.Page;
import com.ak.es.entity.PageSortHighLight;
import com.ak.es.entity.Sort;
import com.ak.es.enums.AggsType;
import com.ak.es.enums.DataType;
import com.ak.es.utils.BeanTools;
import com.ak.es.utils.Constant;
import com.ak.es.utils.IndexTools;
import com.ak.es.utils.JsonUtils;
import com.ak.es.utils.MetaData;
import com.ak.es.utils.Tools;

import lombok.extern.slf4j.Slf4j;

/**
 * @program: AK-ES
 * @description: Elasticsearch基础功能组件实现类
 * @author: AK
 * @create: 2019-01-18 16:04
 **/
@Component
@Slf4j
public class ElasticsearchTemplateImpl<T, M> implements ElasticsearchTemplate<T, M> {

	@Autowired
	RestHighLevelClient client;

	@Override
	public Response request(Request request) throws Exception {
		Response response = client.getLowLevelClient().performRequest(request);
		return response;
	}

	@Override
	public boolean save(T t) throws Exception {
		MetaData metaData = IndexTools.getIndexType(t.getClass());
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		String id = Tools.getESId(t);
		IndexRequest indexRequest = null;
		if (StringUtils.isEmpty(id)) {
			indexRequest = new IndexRequest(indexname, indextype);
		} else {
			indexRequest = new IndexRequest(indexname, indextype, id);
		}
		String source = JsonUtils.obj2String(t);
		indexRequest.source(source, XContentType.JSON);
		IndexResponse indexResponse = null;
		indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
		if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
			log.info("INDEX CREATE SUCCESS");
		} else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
			log.info("INDEX UPDATE SUCCESS");
		} else {
			return false;
		}
		return true;
	}

	@Override
	public BulkResponse save(List<T> list) throws Exception {
		if (list == null || list.size() == 0) {
			return null;
		}
		T t = list.get(0);
		MetaData metaData = IndexTools.getIndexType(t.getClass());
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		BulkRequest rrr = new BulkRequest();
		for (int i = 0; i < list.size(); i++) {
			T tt = list.get(i);
			String id = Tools.getESId(tt);
			// rrr.add(new IndexRequest(indexname, indextype, id)
			// .source(XContentType.JSON, JsonUtils.obj2String(tt)));
			rrr.add(new IndexRequest(indexname, indextype, id).source(BeanTools.objectToMap(tt)));
		}
		BulkResponse bulkResponse = client.bulk(rrr, RequestOptions.DEFAULT);
		return bulkResponse;
	}

	@Override
	public boolean update(T t) throws Exception {
		MetaData metaData = IndexTools.getIndexType(t.getClass());
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		String id = Tools.getESId(t);
		if (StringUtils.isEmpty(id)) {
			throw new Exception("ID cannot be empty");
		}
		UpdateRequest updateRequest = new UpdateRequest(indexname, indextype, id);
		updateRequest.doc(Tools.getFieldValue(t));
		UpdateResponse updateResponse = null;
		updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
		if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
			log.info("INDEX CREATE SUCCESS");
		} else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
			log.info("INDEX UPDATE SUCCESS");
		} else {
			return false;
		}
		return true;
	}

	@Override
	public boolean updateCover(T t) throws Exception {
		return save(t);
	}

	@Override
	public boolean delete(T t) throws Exception {
		MetaData metaData = IndexTools.getIndexType(t.getClass());
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		String id = Tools.getESId(t);
		if (StringUtils.isEmpty(id)) {
			throw new Exception("ID cannot be empty");
		}
		DeleteRequest deleteRequest = new DeleteRequest(indexname, indextype, id);
		DeleteResponse deleteResponse = null;
		deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
		if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) {
			log.info("INDEX DELETE SUCCESS");
		} else {
			return false;
		}
		return true;
	}

	@Override
	public BulkByScrollResponse deleteByCondition(QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		DeleteByQueryRequest request = new DeleteByQueryRequest(indexname);
		request.setQuery(queryBuilder);
		BulkByScrollResponse bulkResponse = client.deleteByQuery(request, RequestOptions.DEFAULT);
		return bulkResponse;
	}

	@Override
	public SearchResponse search(SearchRequest searchRequest) throws IOException {
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		return searchResponse;
	}

	@Override
	public List<T> search(QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return search(queryBuilder, clazz, indexname);
	}

	@Override
	public List<T> search(QueryBuilder queryBuilder, Class<T> clazz, String... indexs) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		List<T> list = new ArrayList<>();
		SearchRequest searchRequest = new SearchRequest(indexs);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(queryBuilder);
		searchSourceBuilder.from(0);
		searchSourceBuilder.size(Constant.DEFALT_PAGE_SIZE);
		searchRequest.source(searchSourceBuilder);
		if (metaData.isPrintLog()) {
			log.info(searchSourceBuilder.toString());
		}
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		SearchHits hits = searchResponse.getHits();
		SearchHit[] searchHits = hits.getHits();
		for (SearchHit hit : searchHits) {
			T t = JsonUtils.string2Obj(hit.getSourceAsString(), clazz);
			list.add(t);
		}
		return list;
	}

	@Override
	public long count(QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return count(queryBuilder, clazz, indexname);
	}

	@Override
	public long count(QueryBuilder queryBuilder, Class<T> clazz, String... indexs) throws Exception {
		CountRequest countRequest = new CountRequest(indexs);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(queryBuilder);
		countRequest.source(searchSourceBuilder);
		CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
		long count = countResponse.getCount();
		return count;
	}

	@Override
	public T getById(M id, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		if (StringUtils.isEmpty(id)) {
			throw new Exception("ID cannot be empty");
		}
		GetRequest getRequest = new GetRequest(indexname, indextype, id.toString());
		GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
		if (getResponse.isExists()) {
			return JsonUtils.string2Obj(getResponse.getSourceAsString(), clazz);
		}
		return null;
	}

	@Override
	public List<T> mgetById(M[] ids, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		MultiGetRequest request = new MultiGetRequest();
		for (int i = 0; i < ids.length; i++) {
			request.add(new MultiGetRequest.Item(indexname, indextype, ids[i].toString()));
		}
		MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);
		List<T> list = new ArrayList<>();
		for (int i = 0; i < response.getResponses().length; i++) {
			MultiGetItemResponse item = response.getResponses()[i];
			GetResponse getResponse = item.getResponse();
			if (getResponse.isExists()) {
				list.add(JsonUtils.string2Obj(getResponse.getSourceAsString(), clazz));
			}
		}
		return list;
	}

	@Override
	public boolean exists(M id, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		if (StringUtils.isEmpty(id)) {
			throw new Exception("ID cannot be empty");
		}
		GetRequest getRequest = new GetRequest(indexname, indextype, id.toString());
		GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
		if (getResponse.isExists()) {
			return true;
		}
		return false;
	}

	@Override
	public Map<Object, Object> aggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz,
			String bucketName) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return aggs(metricName, aggsType, queryBuilder, clazz, bucketName, indexname);
	}

	@Override
	public Map<Object, Object> aggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz,
			String bucketName, String... indexs) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		Field f_bucket = clazz.getDeclaredField(bucketName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (f_bucket == null) {
			throw new Exception("bucket field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		bucketName = genKeyword(f_bucket, bucketName);

		String by = "by_" + bucketName;
		String me = aggsType.toString() + "_" + metricName;

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		TermsAggregationBuilder aggregation = AggregationBuilders.terms(by).field(bucketName);
		// 默认按照聚合结果降序排序
		aggregation.order(BucketOrder.aggregation(me, false));
		if (AggsType.count == aggsType) {
			aggregation.subAggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			aggregation.subAggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			aggregation.subAggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName));
		}
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregation);

		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		if (metaData.isPrintLog()) {
			log.info(searchSourceBuilder.toString());
		}
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

		Aggregations aggregations = searchResponse.getAggregations();
		Terms by_risk_code = aggregations.get(by);
		Map<Object, Object> map = new LinkedHashMap<Object, Object>();
		for (Terms.Bucket bucket : by_risk_code.getBuckets()) {
			if (AggsType.count == aggsType) {
				ValueCount count = bucket.getAggregations().get(me);
				long value = count.getValue();
				map.put(bucket.getKey(), value);
			} else if (AggsType.min == aggsType) {
				ParsedMin min = bucket.getAggregations().get(me);
				double value = min.getValue();
				map.put(bucket.getKey(), value);
			} else if (AggsType.max == aggsType) {
				ParsedMax max = bucket.getAggregations().get(me);
				double value = max.getValue();
				map.put(bucket.getKey(), value);
			} else if (AggsType.sum == aggsType) {
				ParsedSum sum = bucket.getAggregations().get(me);
				double value = sum.getValue();
				map.put(bucket.getKey(), value);
			} else if (AggsType.avg == aggsType) {
				ParsedAvg avg = bucket.getAggregations().get(me);
				double value = avg.getValue();
				map.put(bucket.getKey(), value);
			}
		}
		return map;
	}

	@Override
	public List<Down> aggswith2level(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz,
			String[] bucketNames) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return aggswith2level(metricName, aggsType, queryBuilder, clazz, bucketNames, indexname);
	}

	@Override
	public List<Down> aggswith2level(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz,
			String[] bucketNames, String... indexs) throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (bucketNames == null) {
			throw new NullPointerException();
		}
		if (bucketNames.length != 2) {
			throw new Exception("仅支持两层下钻聚合!");
		}
		Field[] f_buckets = new Field[bucketNames.length];
		for (int i = 0; i < bucketNames.length; i++) {
			f_buckets[i] = clazz.getDeclaredField(bucketNames[i].replaceAll(keyword, ""));
			if (f_buckets[i] == null) {
				throw new Exception("bucket field is null");
			}
		}
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		String me = aggsType.toString() + "_" + metricName;

		String[] bys = new String[bucketNames.length];
		for (int i = 0; i < f_buckets.length; i++) {
			bucketNames[i] = genKeyword(f_buckets[i], bucketNames[i]);
			bys[i] = "by_" + bucketNames[i];
		}
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		TermsAggregationBuilder[] termsAggregationBuilders = new TermsAggregationBuilder[bucketNames.length];
		for (int i = 0; i < bucketNames.length; i++) {
			TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms(bys[i]).field(bucketNames[i]);
			termsAggregationBuilders[i] = aggregationBuilder;
		}
		for (int i = 0; i < termsAggregationBuilders.length; i++) {
			if (i != termsAggregationBuilders.length - 1) {
				termsAggregationBuilders[i].subAggregation(termsAggregationBuilders[i + 1]);
			}
		}
		if (AggsType.count == aggsType) {
			termsAggregationBuilders[termsAggregationBuilders.length - 1]
					.subAggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			termsAggregationBuilders[termsAggregationBuilders.length - 1]
					.subAggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			termsAggregationBuilders[termsAggregationBuilders.length - 1]
					.subAggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			termsAggregationBuilders[termsAggregationBuilders.length - 1]
					.subAggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			termsAggregationBuilders[termsAggregationBuilders.length - 1]
					.subAggregation(AggregationBuilders.avg(me).field(metricName));
		}
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(termsAggregationBuilders[0]);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		// 下面不支持2层以上的下钻
		List<Down> downList = new ArrayList<>();
		Terms terms1 = searchResponse.getAggregations().get(bys[0]);
		Terms terms2;
		for (Terms.Bucket bucket : terms1.getBuckets()) {
			terms2 = bucket.getAggregations().get(bys[1]);
			for (Terms.Bucket bucket2 : terms2.getBuckets()) {
				Down down = new Down();
				down.setLevel_1_key(bucket.getKey().toString());
				down.setLevel_2_key(bucket2.getKey().toString());
				if (AggsType.count == aggsType) {
					ValueCount count = bucket2.getAggregations().get(me);
					long value = count.getValue();
					down.setValue(value);
				} else if (AggsType.min == aggsType) {
					ParsedMin min = bucket2.getAggregations().get(me);
					double value = min.getValue();
					down.setValue(value);
				} else if (AggsType.max == aggsType) {
					ParsedMax max = bucket2.getAggregations().get(me);
					double value = max.getValue();
					down.setValue(value);
				} else if (AggsType.sum == aggsType) {
					ParsedSum sum = bucket2.getAggregations().get(me);
					double value = sum.getValue();
					down.setValue(value);
				} else if (AggsType.avg == aggsType) {
					ParsedAvg avg = bucket2.getAggregations().get(me);
					double value = avg.getValue();
					down.setValue(value);
				}
				downList.add(down);
			}
		}
		return downList;
	}

	private static final String keyword = ".keyword";

	/**
	 * 组织字段是否带有.keyword
	 *
	 * @param field
	 * @param name
	 * @return
	 */
	private String genKeyword(Field field, String name) {
		ESMapping esMapping = field.getAnnotation(ESMapping.class);
		// 带着.keyword直接忽略
		if (name == null || name.indexOf(keyword) > -1) {
			return name;
		}
		// 只要keyword是true就要拼接
		// 没配注解，但是类型是字符串，默认keyword是true
		if (esMapping == null) {
			if (field.getType() == String.class) {
				return name + keyword;
			}
		}
		// 配了注解，但是类型是字符串，默认keyword是true
		else {
			if (esMapping.datatype() == DataType.text_type && esMapping.keyword() == true) {
				return name + keyword;
			}
		}
		return name;
	}

	@Override
	public double aggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz)
			throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return aggs(metricName, aggsType, queryBuilder, clazz, indexname);
	}

	@Override
	public double aggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder, Class<T> clazz,
			String... indexs) throws Exception {
		String[] indexname = indexs;
		String me = aggsType.toString() + "_" + metricName;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		if (AggsType.count == aggsType) {
			searchSourceBuilder.aggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			searchSourceBuilder.aggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			searchSourceBuilder.aggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			searchSourceBuilder.aggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			searchSourceBuilder.aggregation(AggregationBuilders.avg(me).field(metricName));
		}
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		if (AggsType.count == aggsType) {
			ValueCount count = searchResponse.getAggregations().get(me);
			long value = count.getValue();
			return Double.parseDouble(String.valueOf(value));
		} else if (AggsType.min == aggsType) {
			ParsedMin min = searchResponse.getAggregations().get(me);
			double value = min.getValue();
			return value;
		} else if (AggsType.max == aggsType) {
			ParsedMax max = searchResponse.getAggregations().get(me);
			double value = max.getValue();
			return value;
		} else if (AggsType.sum == aggsType) {
			ParsedSum sum = searchResponse.getAggregations().get(me);
			double value = sum.getValue();
			return value;
		} else if (AggsType.avg == aggsType) {
			ParsedAvg avg = searchResponse.getAggregations().get(me);
			double value = avg.getValue();
			return value;
		}
		return 0d;
	}

	@Override
	public Stats statsAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return statsAggs(metricName, queryBuilder, clazz, indexname);
	}

	@Override
	public Stats statsAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz, String... indexs)
			throws Exception {
		String[] indexname = indexs;
		String me = "stats";
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		StatsAggregationBuilder aggregation = AggregationBuilders.stats(me).field(metricName);
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		Stats stats = searchResponse.getAggregations().get(me);
		return stats;
	}

	@Override
	public Map<String, Stats> statsAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz, String bucketName)
			throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return statsAggs(metricName, queryBuilder, clazz, bucketName, indexname);
	}

	@Override
	public Map<String, Stats> statsAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz, String bucketName,
			String... indexs) throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		Field f_bucket = clazz.getDeclaredField(bucketName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (f_bucket == null) {
			throw new Exception("bucket field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		bucketName = genKeyword(f_bucket, bucketName);

		String by = "by_" + bucketName;
		String me = "stats" + "_" + metricName;

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		TermsAggregationBuilder aggregation = AggregationBuilders.terms(by).field(bucketName);
		// 默认按照count的降序排序
		aggregation.order(BucketOrder.count(false));
		aggregation.subAggregation(AggregationBuilders.stats(me).field(metricName));
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

		Aggregations aggregations = searchResponse.getAggregations();
		Terms by_risk_code = aggregations.get(by);
		Map<String, Stats> map = new LinkedHashMap<>();
		for (Terms.Bucket bucket : by_risk_code.getBuckets()) {
			Stats stats = bucket.getAggregations().get(me);
			map.put(bucket.getKey().toString(), stats);
		}
		return map;
	}

	@Override
	public Aggregations aggs(AggregationBuilder aggregationBuilder, QueryBuilder queryBuilder, Class<T> clazz)
			throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return aggs(aggregationBuilder, queryBuilder, clazz, indexname);
	}

	@Override
	public Aggregations aggs(AggregationBuilder aggregationBuilder, QueryBuilder queryBuilder, Class<T> clazz,
			String... indexs) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = indexs;
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregationBuilder);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		if (metaData.isPrintLog()) {
			log.info(searchSourceBuilder.toString());
		}
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		return searchResponse.getAggregations();
	}

	@Override
	public long cardinality(String metricName, QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return cardinality(metricName, queryBuilder, clazz, indexname);
	}

	@Override
	public long cardinality(String metricName, QueryBuilder queryBuilder, Class<T> clazz, String... indexs)
			throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		String me = "cardinality_" + metricName;
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		CardinalityAggregationBuilder aggregation = AggregationBuilders.cardinality(me).field(metricName);
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		Cardinality agg = searchResponse.getAggregations().get(me);
		return agg.getValue();
	}

	@Override
	public Map<Double, Double> percentilesAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz)
			throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return percentilesAggs(metricName, queryBuilder, clazz, Constant.DEFAULT_PERCSEGMENT, indexname);
	}

	@Override
	public Map<Double, Double> percentilesAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz,
			double[] customSegment, String... indexs) throws Exception {
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (customSegment == null) {
			throw new Exception("customSegment is null");
		} else if (customSegment.length == 0) {
			throw new Exception("customSegment is null");
		}
		metricName = genKeyword(f_metric, metricName);
		String me = "percentiles_" + metricName;
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		PercentilesAggregationBuilder aggregation = AggregationBuilders.percentiles(me).field(metricName)
				.percentiles(customSegment);
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexs);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		Map<Double, Double> map = new LinkedHashMap<Double, Double>();
		Percentiles agg = searchResponse.getAggregations().get(me);
		for (Percentile entry : agg) {
			double percent = entry.getPercent();
			double value = entry.getValue();
			map.put(percent, value);
		}
		return map;
	}

	@Override
	public Map<Double, Double> percentileRanksAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz,
			double[] customSegment) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return percentileRanksAggs(metricName, queryBuilder, clazz, customSegment, indexname);
	}

	@Override
	public Map<Double, Double> percentileRanksAggs(String metricName, QueryBuilder queryBuilder, Class<T> clazz,
			double[] customSegment, String... indexs) throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (customSegment == null || customSegment.length == 0) {
			throw new Exception("customSegment is null");
		}
		metricName = genKeyword(f_metric, metricName);
		String me = "percentiles_" + metricName;
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		PercentileRanksAggregationBuilder aggregation = AggregationBuilders.percentileRanks(me, customSegment)
				.field(metricName);
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.size(0);
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		Map<Double, Double> map = new LinkedHashMap<Double, Double>();
		PercentileRanks agg = searchResponse.getAggregations().get(me);
		for (Percentile entry : agg) {
			double percent = entry.getPercent();
			double value = entry.getValue();
			map.put(percent, value);
		}
		return map;
	}

	@Override
	public Map<Object, Object> filterAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, FiltersAggregator.KeyedFilter[] filters) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return filterAggs(metricName, aggsType, queryBuilder, clazz, filters, indexname);
	}

	@Override
	public Map<Object, Object> filterAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, FiltersAggregator.KeyedFilter[] filters, String... indexs) throws Exception {
		String[] indexname = indexs;
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		if (filters == null) {
			throw new NullPointerException();
		}
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		String me = aggsType.toString() + "_" + metricName;
		AggregationBuilder aggregation = AggregationBuilders.filters("filteragg", filters);
		searchSourceBuilder.size(0);
		if (AggsType.count == aggsType) {
			aggregation.subAggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			aggregation.subAggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			aggregation.subAggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName));
		}
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		Filters agg = searchResponse.getAggregations().get("filteragg");
		Map<Object, Object> map = new LinkedHashMap<Object, Object>();
		for (Filters.Bucket entry : agg.getBuckets()) {
			if (AggsType.count == aggsType) {
				ValueCount count = entry.getAggregations().get(me);
				long value = count.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.min == aggsType) {
				ParsedMin min = entry.getAggregations().get(me);
				double value = min.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.max == aggsType) {
				ParsedMax max = entry.getAggregations().get(me);
				double value = max.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.sum == aggsType) {
				ParsedSum sum = entry.getAggregations().get(me);
				double value = sum.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.avg == aggsType) {
				ParsedAvg avg = entry.getAggregations().get(me);
				double value = avg.getValue();
				map.put(entry.getKey(), value);
			}
		}
		return map;
	}

	@Override
	public Map<Object, Object> histogramAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, String bucketName, double interval) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return histogramAggs(metricName, aggsType, queryBuilder, clazz, bucketName, interval, indexname);
	}

	@Override
	public Map<Object, Object> histogramAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, String bucketName, double interval, String... indexs) throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		Field f_bucket = clazz.getDeclaredField(bucketName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (f_bucket == null) {
			throw new Exception("bucket field is null");
		}
		metricName = genKeyword(f_metric, metricName);
		bucketName = genKeyword(f_bucket, bucketName);
		String by = "by_" + bucketName;
		String me = aggsType.toString() + "_" + metricName;

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		AggregationBuilder aggregation = AggregationBuilders.histogram(by).field(bucketName).interval(interval);
		searchSourceBuilder.size(0);
		if (AggsType.count == aggsType) {
			aggregation.subAggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			aggregation.subAggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			aggregation.subAggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName));
		}
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		ParsedHistogram agg = searchResponse.getAggregations().get(by);
		Map<Object, Object> map = new LinkedHashMap<Object, Object>();
		for (Histogram.Bucket entry : agg.getBuckets()) {
			if (AggsType.count == aggsType) {
				ValueCount count = entry.getAggregations().get(me);
				long value = count.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.min == aggsType) {
				ParsedMin min = entry.getAggregations().get(me);
				double value = min.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.max == aggsType) {
				ParsedMax max = entry.getAggregations().get(me);
				double value = max.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.sum == aggsType) {
				ParsedSum sum = entry.getAggregations().get(me);
				double value = sum.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.avg == aggsType) {
				ParsedAvg avg = entry.getAggregations().get(me);
				double value = avg.getValue();
				map.put(entry.getKey(), value);
			}
		}
		return map;
	}

	@Override
	public Map<Object, Object> dateHistogramAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, String bucketName, DateHistogramInterval interval) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return dateHistogramAggs(metricName, aggsType, queryBuilder, clazz, bucketName, interval, indexname);
	}

	@Override
	public Map<Object, Object> dateHistogramAggs(String metricName, AggsType aggsType, QueryBuilder queryBuilder,
			Class<T> clazz, String bucketName, DateHistogramInterval interval, String... indexs) throws Exception {
		String[] indexname = indexs;
		Field f_metric = clazz.getDeclaredField(metricName.replaceAll(keyword, ""));
		Field f_bucket = clazz.getDeclaredField(bucketName.replaceAll(keyword, ""));
		if (f_metric == null) {
			throw new Exception("metric field is null");
		}
		if (f_bucket == null) {
			throw new Exception("bucket field is null");
		} else if (f_bucket.getType() != Date.class) {
			throw new Exception("bucket type is not support");
		}
		ESMapping esMapping = f_bucket.getAnnotation(ESMapping.class);
		if (esMapping != null && esMapping.datatype() != DataType.date_type) {
			throw new Exception("bucket type is not support");
		}
		metricName = genKeyword(f_metric, metricName);
		bucketName = genKeyword(f_bucket, bucketName);
		String by = "by_" + bucketName;
		String me = aggsType.toString() + "_" + metricName;

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		AggregationBuilder aggregation = AggregationBuilders.dateHistogram(by).field(bucketName)
				.dateHistogramInterval(interval);
		searchSourceBuilder.size(0);
		if (AggsType.count == aggsType) {
			aggregation.subAggregation(AggregationBuilders.count(me).field(metricName));
		} else if (AggsType.min == aggsType) {
			aggregation.subAggregation(AggregationBuilders.min(me).field(metricName));
		} else if (AggsType.max == aggsType) {
			aggregation.subAggregation(AggregationBuilders.max(me).field(metricName));
		} else if (AggsType.sum == aggsType) {
			aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName));
		} else if (AggsType.avg == aggsType) {
			aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName));
		}
		if (queryBuilder != null) {
			searchSourceBuilder.query(queryBuilder);
		}
		searchSourceBuilder.aggregation(aggregation);
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		ParsedDateHistogram agg = searchResponse.getAggregations().get(by);
		Map<Object, Object> map = new LinkedHashMap<Object, Object>();
		for (Histogram.Bucket entry : agg.getBuckets()) {
			if (AggsType.count == aggsType) {
				ValueCount count = entry.getAggregations().get(me);
				long value = count.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.min == aggsType) {
				ParsedMin min = entry.getAggregations().get(me);
				double value = min.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.max == aggsType) {
				ParsedMax max = entry.getAggregations().get(me);
				double value = max.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.sum == aggsType) {
				ParsedSum sum = entry.getAggregations().get(me);
				double value = sum.getValue();
				map.put(entry.getKey(), value);
			} else if (AggsType.avg == aggsType) {
				ParsedAvg avg = entry.getAggregations().get(me);
				double value = avg.getValue();
				map.put(entry.getKey(), value);
			}
		}
		return map;
	}

	@Override
	public boolean deleteById(M id, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String indexname = metaData.getIndexname();
		String indextype = metaData.getIndextype();
		if (StringUtils.isEmpty(id)) {
			throw new Exception("ID cannot be empty");
		}
		DeleteRequest deleteRequest = new DeleteRequest(indexname, indextype, id.toString());
		DeleteResponse deleteResponse = null;
		deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
		if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) {
			log.info("INDEX DELETE SUCCESS");
		} else {
			return false;
		}
		return true;
	}

	@Override
	public Page<T> search(Page<T> pageList, QueryBuilder queryBuilder, PageSortHighLight pageSortHighLight,
			Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		if (pageSortHighLight == null) {
			throw new NullPointerException("PageSortHighLight不能为空!");
		}
		return search(pageList, queryBuilder, pageSortHighLight, clazz, indexname);
	}

	@Override
	public Page<T> search(Page<T> pageList, QueryBuilder queryBuilder, PageSortHighLight pageSortHighLight,
			Class<T> clazz, String... indexs) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		List<T> list = new ArrayList<>();
		if (pageSortHighLight == null) {
			throw new NullPointerException("PageSortHighLight不能为空!");
		}
		SearchRequest searchRequest = new SearchRequest(indexs);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(queryBuilder);
		// 分页
		searchSourceBuilder.from((pageSortHighLight.getCurrentPage() - 1) * pageSortHighLight.getPageSize());
		searchSourceBuilder.size(pageSortHighLight.getPageSize());
		// 排序
		Sort sort = pageSortHighLight.getSort();
		List<Sort.Order> orders = sort.listOrders();
		orders.forEach(order -> searchSourceBuilder
				.sort(new FieldSortBuilder(order.getProperty()).order(order.getDirection())));
		// 高亮
		HighLight highLight = pageSortHighLight.getHighLight();
		boolean highLightFlag = false;
		if (highLight.getHighLightList() != null && highLight.getHighLightList().size() != 0) {
			HighlightBuilder highlightBuilder = new HighlightBuilder();
			if (!StringUtils.isEmpty(highLight.getPreTag()) && !StringUtils.isEmpty(highLight.getPostTag())) {
				highlightBuilder.preTags(highLight.getPreTag());
				highlightBuilder.postTags(highLight.getPostTag());
			}
			for (int i = 0; i < highLight.getHighLightList().size(); i++) {
				highLightFlag = true;
				// You can set fragment_size to 0 to never split any sentence.
				// 不对高亮结果进行拆分
				highlightBuilder.field(highLight.getHighLightList().get(i), 0);
			}
			searchSourceBuilder.highlighter(highlightBuilder);
		}
		searchRequest.source(searchSourceBuilder);
		if (metaData.isPrintLog()) {
			log.info(searchSourceBuilder.toString());
		}
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		SearchHits hits = searchResponse.getHits();
		SearchHit[] searchHits = hits.getHits();
		for (SearchHit hit : searchHits) {
			T t = JsonUtils.string2Obj(hit.getSourceAsString(), clazz);
			// 替换高亮字段
			if (highLightFlag) {
				Map<String, HighlightField> hmap = hit.getHighlightFields();
				hmap.forEach((k, v) -> {
					try {
						Object obj = mapToObject(hmap, clazz);
						BeanUtils.copyProperties(obj, t, BeanTools.getNoValuePropertyNames(obj));
					} catch (Exception e) {
						e.printStackTrace();
					}
				});
			}
			list.add(t);
		}

		pageList.setList(list);
		pageList.setTotalElements(hits.totalHits);
		pageList.setTotalPages(getTotalPages(hits.totalHits, pageSortHighLight.getPageSize()));
		return pageList;
	}

	private Object mapToObject(Map<String, ?> map, Class<?> beanClass) throws Exception {
		if (map == null)
			return null;
		Object obj = beanClass.newInstance();
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (map.get(field.getName()) != null && !StringUtils.isEmpty(map.get(field.getName()))) {
				int mod = field.getModifiers();
				if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
					continue;
				}
				field.setAccessible(true);
				if (map.get(field.getName()) instanceof HighlightField
						&& ((HighlightField) map.get(field.getName())).fragments().length > 0) {
					field.set(obj, ((HighlightField) map.get(field.getName())).fragments()[0].string());
				}
			}
		}
		return obj;
	}

	@Override
	public List<T> scroll(QueryBuilder queryBuilder, Class<T> clazz, long time, String... indexs) throws Exception {
		if (queryBuilder == null) {
			throw new NullPointerException();
		}
		String[] indexname = indexs;
		List<T> list = new ArrayList<>();
		Scroll scroll = new Scroll(TimeValue.timeValueHours(time));
		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.scroll(scroll);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(queryBuilder);
		searchSourceBuilder.size(Constant.DEFAULT_SCROLL_PERPAGE);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		String scrollId = searchResponse.getScrollId();
		SearchHit[] searchHits = searchResponse.getHits().getHits();
		// 第一把查询出的结果
		for (SearchHit hit : searchHits) {
			T t = JsonUtils.string2Obj(hit.getSourceAsString(), clazz);
			list.add(t);
		}
		while (searchHits != null && searchHits.length > 0) {
			SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
			scrollRequest.scroll(scroll);
			searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
			scrollId = searchResponse.getScrollId();
			searchHits = searchResponse.getHits().getHits();
			for (SearchHit hit : searchHits) {
				T t = JsonUtils.string2Obj(hit.getSourceAsString(), clazz);
				list.add(t);
			}
		}
		ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
		clearScrollRequest.addScrollId(scrollId);
		ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
		clearScrollResponse.isSucceeded();
		return list;
	}

	@Override
	public List<T> scroll(QueryBuilder queryBuilder, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return scroll(queryBuilder, clazz, Constant.DEFAULT_SCROLL_TIME, indexname);
	}

	@Override
	public List<String> completionSuggest(String fieldName, String fieldValue, Class<T> clazz) throws Exception {
		MetaData metaData = IndexTools.getIndexType(clazz);
		String[] indexname = metaData.getSearchIndexNames();
		return completionSuggest(fieldName, fieldValue, clazz, indexname);
	}

	@Override
	public List<String> completionSuggest(String fieldName, String fieldValue, Class<T> clazz, String... indexs)
			throws Exception {
		String[] indexname = indexs;

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		SuggestBuilder suggestBuilder = new SuggestBuilder();

		CompletionSuggestionBuilder completionSuggestionBuilder = new CompletionSuggestionBuilder(
				fieldName + ".suggest");
		completionSuggestionBuilder.text(fieldValue);
		completionSuggestionBuilder.size(Constant.COMPLETION_SUGGESTION_SIZE);
		suggestBuilder.addSuggestion("suggest_" + fieldName, completionSuggestionBuilder);
		searchSourceBuilder.suggest(suggestBuilder);

		SearchRequest searchRequest = new SearchRequest(indexname);
		searchRequest.source(searchSourceBuilder);
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

		Suggest suggest = searchResponse.getSuggest();
		if (suggest == null) {
			return null;
		}
		CompletionSuggestion completionSuggestion = suggest.getSuggestion("suggest_" + fieldName);
		List<String> list = new ArrayList<>();
		for (CompletionSuggestion.Entry entry : completionSuggestion.getEntries()) {
			for (CompletionSuggestion.Entry.Option option : entry) {
				String suggestText = option.getText().string();
				list.add(suggestText);
			}
		}
		return list;
	}

	private int getTotalPages(long totalHits, int pageSize) {
		return pageSize == 0 ? 1 : (int) Math.ceil((double) totalHits / (double) pageSize);
	}
}
